package cn.fscode.code.generator.service.impl;

import cn.fscode.code.generator.assembly.DatasourceConfigAssembly;
import cn.fscode.code.generator.assembly.GenTableAssembly;
import cn.fscode.code.generator.common.enums.GenType;
import cn.fscode.code.generator.gen.*;
import cn.fscode.code.generator.manager.DocManager;
import cn.fscode.code.generator.manager.WordTableManager;
import cn.fscode.code.generator.model.*;
import cn.fscode.code.generator.model.po.DatasourceConfigPO;
import cn.fscode.code.generator.model.po.GenTableColumnPO;
import cn.fscode.code.generator.model.po.GenTableIndexPO;
import cn.fscode.code.generator.model.po.GenTablePO;
import cn.fscode.code.generator.model.req.*;
import cn.fscode.code.generator.model.resp.GenInfoResp;
import cn.fscode.code.generator.model.resp.GenTableResp;
import cn.fscode.code.generator.model.resp.ImportTableResp;
import cn.fscode.code.generator.repository.DatasourceConfigRepository;
import cn.fscode.code.generator.repository.GenTableColumnRepository;
import cn.fscode.code.generator.repository.GenTableIndexRepository;
import cn.fscode.code.generator.repository.GenTableRepository;
import cn.fscode.code.generator.service.IGenTableService;
import cn.fscode.code.generator.test.model.GenSqlConfig;
import cn.fscode.code.generator.test.utils.VelocityInitializer;
import cn.fscode.code.generator.test.utils.VelocityUtils;
import cn.fscode.code.generator.utils.TableUtils;
import cn.fscode.commons.tool.core.CollectionUtils;
import cn.fscode.commons.tool.core.StringUtils;
import cn.fscode.commons.tool.core.exception.Assert;
import cn.fscode.commons.tool.core.exception.BizException;
import cn.fscode.commons.tool.core.file.FileUtils;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author shenguangyang
 */
@Slf4j
@Service
public class GenTableServiceImpl implements IGenTableService {
    @Resource
    private DocManager docManager;
    @Resource
    private GenTableRepository genTableRepository;
    @Resource
    private GenTableColumnRepository genTableColumnRepository;
    @Resource
    private ApplicationContext applicationContext;
    @Resource
    private DatasourceConfigRepository datasourceConfigRepository;
    @Resource
    private GenTableAssembly genTableAssembly;
    @Resource
    private DatasourceConfigAssembly datasourceConfigAssembly;
    @Resource
    private GenTableIndexRepository genTableIndexRepository;

    private final Map<DbType, DocumentTableHandler> tableDialectHandlerMap = new ConcurrentHashMap<>();

    private GenSqlConfig genSqlConfig;

    @PostConstruct
    public void init() {
        genSqlConfig = new GenSqlConfig();
        List<String> extendColumnList = new ArrayList<>();
        extendColumnList.add("`patientId` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '病人信息表的主键id，没有时默认为0'");
        genSqlConfig.setExtendColumn(extendColumnList);

        applicationContext.getBeansOfType(DocumentTableHandler.class)
                .forEach((k, v) -> tableDialectHandlerMap.put(v.dbType(), v));
    }

    /**
     * 初始化表格的索引信息, 为了读取表格中数据 (必填)
     */
    private Map<String, Integer> initRequiredTableIndexMap(ImportDocumentTableReq req) {
        Map<String, Integer> indexMap = new HashMap<>();
        indexMap.put(req.getColumnCommentCol(), -1);
        indexMap.put(req.getColumnNameCol(), -1);
        indexMap.put(req.getColumnTypeCol(), -1);
        indexMap.put(req.getRequiredCol(), -1);
        indexMap.put(req.getTableIndexCol(), -1);
        return indexMap;
    }

    private void checkRequiredTableIndexMap(Map<String, Integer> indexMap) {
        Set<String> collect = indexMap.entrySet().stream().filter(e -> e.getValue() == -1)
                .map(Map.Entry::getKey).collect(Collectors.toSet());
        if (CollectionUtils.isNotEmpty(collect)) {
            throw new BizException("文档中表列名不包含所指定的列名: {}", collect);
        }
    }

    @Override
    @Transactional
    public List<ImportTableResp> importWordTable(ImportDocumentTableReq req, WordTableManager wordTableManager) {
        DatasourceConfigPO datasourceConfigPO = datasourceConfigRepository.getById(req.getDatasourceId());
        Assert.notNull(datasourceConfigPO, "数据源不存在");
        GeneratorConfig generatorConfig = datasourceConfigAssembly.toGeneratorConfigFrom(datasourceConfigPO);
        DocumentTableHandler documentTableHandler = DocumentTableFactory.build(generatorConfig);
        File localFile = new File(req.getFilePath());
        try {
            List<WordDataUnderTitle> wordDataList = wordTableManager.getWordDataList(localFile.getAbsolutePath());
            Map<String, Integer> requiredIndexMap = initRequiredTableIndexMap(req);

            List<ImportTable> importTableList = new CopyOnWriteArrayList<>();

            // 遍历工作簿中的所有数据
            Stream<WordDataUnderTitle> stream = wordDataList.stream().filter(x -> x.getTableNum() > 0);
            stream.forEach((item) -> {
                String title = StringUtils.deleteWhitespace(item.getTitle().trim());
                String tableName = TableUtils.getTableName(item.getTitle().trim());
                if (StringUtils.isEmpty(tableName)) {
                    log.error("title: {}", title);
                    throw new BizException("word中有多余表格或者有非法标题命名方式, title: {}", title);
                }
                WordTable wordTable = item.getWordTableList().get(0);
                ImportTable importTable = new ImportTable();
                GenTablePO genTablePO = GenTablePO.builder()
                        .tplCategory(GenType.CRUD.getCode()).datasourceId(datasourceConfigPO.getId())
                        .functionName(title.replace(tableName, "")).tableName(tableName)
                        // 标题中表名去掉就相当于表描述了
                        .tableComment(title.replace(tableName, "")).build();
                importTable.setGenTable(genTablePO);
                // 创建主键列
                GenTableColumnPO pkColumn = GenTableColumnPO.builder()
                        .columnComment("主键").columnLength(req.getPkLength()).isPk(true)
                        .columnName(req.getPkName()).columnType(req.getPkType()).isNullable(false)
                        .columnScale(0).columnLength(req.getPkLength()).sort(1).build();
                importTable.getGenTableColumnList().add(pkColumn);

                int maxRow = wordTable.getRows().size();

                for (int row = 0; row < maxRow; row++) {
                    TableColumn column = new TableColumn();
                    Map<String, Consumer<String>> setMap = new HashMap<>();
                    setMap.put(req.getColumnCommentCol(), column::setColumnComment);
                    setMap.put(req.getColumnTypeCol(), column::setColumnType);
                    setMap.put(req.getColumnNameCol(), column::setColumnName);
                    setMap.put(req.getRequiredCol(), column::setIsRequiredFlag);

                    // 获取最后单元格num，即总单元格数 ***注意：此处从1开始计数***
                    int maxRol = wordTable.getRows().get(row).getCells().size();
                    for (int rol = 0; rol < maxRol; rol++) {
                        String data = wordTable.getRows().get(row).getCells().get(rol).getText();
                        if (StringUtils.isEmpty(data)) {
                            continue;
                        }
                        int finalRol = rol;
                        if (row == 0) {
                            requiredIndexMap.keySet().stream()
                                    .filter(k -> StrUtil.containsAnyIgnoreCase(data.trim(), k.trim()))
                                    .forEach(k -> requiredIndexMap.put(k, finalRol));
                        } else {
                            String colName = requiredIndexMap.entrySet().stream().filter(e -> finalRol == e.getValue())
                                    .map(Map.Entry::getKey).findFirst().orElse("");
                            if (StringUtils.isNotEmpty(colName) && setMap.containsKey(colName)) {
                                setMap.get(colName).accept(data);
                            }
                        }
                    }

                    if (row > 0 && StrUtil.isAllNotEmpty(column.getColumnName(), column.getColumnType())) {
                        column.fillIsRequired(req.getRequiredFlag());
                        GenTableColumnPO columnPO = genTableAssembly.from(column);
                        columnPO.setSort(row + 1);
                        importTable.getGenTableColumnList().add(columnPO);
                    }

                    if(row == 0) {
                        // 校验指定的列名和表中列名是否存在
                        checkRequiredTableIndexMap(requiredIndexMap);
                    }
                }

                Map<String, Integer> requiredIndexTempMap = new HashMap<>();
                requiredIndexTempMap.put(req.getTableIndexCol(), -1);
                // 默认为联合索引
                List<GenTableIndexPO> tempGenTableIndexList = new ArrayList<>();
                for (int row = 0; row < maxRow; row++) {
                    GenTableIndexPO genTableIndex = new GenTableIndexPO();
                    int maxRol = wordTable.getRows().get(row).getCells().size();
                    Map<String, Consumer<String>> setMap = new HashMap<>();
                    setMap.put(req.getTableIndexCol(), genTableIndex::setColumnNames);
                    // 收集表索引字段
                    for (int rol = 0; rol < maxRol; rol++) {
                        String data = wordTable.getRows().get(row).getCells().get(rol).getText();
                        if (StringUtils.isEmpty(data)) {
                            continue;
                        }
                        int finalRol = rol;
                        if (row == 0) {
                            requiredIndexTempMap.keySet().stream()
                                    .filter(k -> StrUtil.containsAnyIgnoreCase(data.trim(), k.trim()))
                                    .forEach(k -> requiredIndexTempMap.put(k, finalRol));
                        } else {
                            String colName = requiredIndexTempMap.entrySet().stream().filter(e -> finalRol == e.getValue())
                                    .map(Map.Entry::getKey).findFirst().orElse("");
                            if (StringUtils.isNotEmpty(colName) && req.getTableIndexFlag().equalsIgnoreCase(data)
                                    && setMap.containsKey(colName)) {
                                setMap.get(colName).accept(importTable.getGenTableColumnList().get(row).getColumnName());
                                break;
                            }
                        }
                    }
                    if (StringUtils.isNotEmpty(genTableIndex.getColumnNames())) {
                        tempGenTableIndexList.add(genTableIndex);
                    }
                }
                // 合并联合索引
                List<String> columnNames = tempGenTableIndexList.stream()
                        .map(GenTableIndexPO::getColumnNames).collect(Collectors.toList());
                if (CollectionUtils.isNotEmpty(columnNames)) {
                    GenTableIndexPO genTableIndex = new GenTableIndexPO();
                    genTableIndex.setName("idx_biz");
                    genTableIndex.setType(req.getIndexType());
                    genTableIndex.setMethod(req.getIndexMethod());
                    genTableIndex.setColumnNames(StringUtils.toStr(columnNames, ","));
                    importTable.getGenTableIndexList().add(genTableIndex);
                }
                importTableList.add(importTable);
            });


            importTableList.stream()
                    .flatMap(x -> x.getGenTableColumnList().stream())
                    .forEach(column -> {
                        ColumnUnit columnUnit = documentTableHandler.getColumnUnit(column.getColumnType());
                        genTableAssembly.updateGenTableColumnPO(columnUnit, column);
                    });
            return batchSaveGenTable(importTableList);
        } catch (IOException e) {
            log.error("errorMessage: {}", e.getMessage());
            log.debug("error: ", e);
            throw new BizException("导入失败");
        } finally {
            FileUtils.deleteFile(localFile.getAbsolutePath());
        }
    }

    public List<ImportTableResp> batchSaveGenTable(List<ImportTable> importTableList) {
        List<ImportTableResp> respList = new ArrayList<>();
        // 执行保存操作
        for (ImportTable item : importTableList) {
            GenTablePO genTable = item.getGenTable();
            List<GenTableColumnPO> genTableColumnList = item.getGenTableColumnList();
            genTableRepository.saveOrUpdateGenTable(genTable);
            genTableColumnRepository.saveColumnBatch(genTable, genTableColumnList);
            List<GenTableIndexPO> genTableIndexList = item.getGenTableIndexList().stream()
                    .peek(x -> x.setTableId(item.getGenTable().getId()))
                    .collect(Collectors.toList());
            genTableIndexRepository.deleteByTableId(genTable.getId());
            genTableIndexRepository.saveOrUpdateBatch(genTableIndexList);
            ImportTableResp resp = ImportTableResp.builder()
                    .tableId(genTable.getId())
                    .tableName(genTable.getTableName())
                    .tableComment(genTable.getTableComment()).build();
            respList.add(resp);
        }
        return respList;
    }

    @Override
    public void importTableFromExcel() {

    }

    @Override
    public Map<String, String> previewCode(List<Long> tableIds) {
        Map<String, String> dataMap = new HashMap<>();
        if (CollectionUtils.isEmpty(tableIds)) {
            return dataMap;
        }

        VelocityInitializer.initVelocity();
        GenTablePO genTablePO = genTableRepository.getById(tableIds.get(0));
        genTablePO.setColumns(genTableColumnRepository.listByTableId(genTablePO.getId()));

        VelocityContext context = VelocityUtils.prepareContext(genTablePO);
        // 获取模板列表
        List<String> templates = VelocityUtils.getTemplateList(genTablePO.getTplCategory());
        for (String template : templates) {
            // 渲染模板
            StringWriter sw = new StringWriter();
            Template tpl = Velocity.getTemplate(template, StandardCharsets.UTF_8.name());
            tpl.merge(context, sw);
            dataMap.put(template, sw.toString());
        }
        return dataMap;
    }


//    public List<GenTableColumnPO> getGenTableFromExcel() {
//        try {
//            //创建工作簿对象
//            XSSFWorkbook xssfWorkbook = new XSSFWorkbook(Files.newInputStream(Paths.get("D:\\temp.xlsx")));
//            //获取工作簿下sheet的个数
//            int sheetNum = xssfWorkbook.getNumberOfSheets();
//            System.out.println("该excel文件中总共有：" + sheetNum + " 个sheet");
//            CodeGenProperties.GenSql genSql = properties.getGenSql();
//
//            Map<String, Integer> indexMap = initTableIndexMap();
//
//            List<GenTableColumnPO> genTableColumnPOList = new CopyOnWriteArrayList<>();
//
//            // 遍历工作簿中的所有数据
//            for (int i = 0; i < sheetNum; i++) {
//                //读取第i个工作表
////                System.out.println("读取第" + (i + 1) + "个sheet");
//                XSSFSheet sheet = xssfWorkbook.getSheetAt(i);
//                //获取最后一行的num，即总行数。此处从0开始
//                int maxRow = sheet.getLastRowNum();
//
//                for (int row = 0; row <= maxRow; row++) {
//                    GenTableColumnPO column = new GenTableColumnPO();
//                    Map<String, Consumer<String>> setMap = new HashMap<>();
//                    setMap.put(genSql.getColumnCommentCol(), column::setColumnComment);
//                    setMap.put(genSql.getColumnNameCol(), column::setColumnName);
//                    setMap.put(genSql.getColumnTypeCol(), column::setColumnType);
//                    // 需要转换
//                    setMap.put(LambdaUtil.getFieldName(column::getFieldName), column::setFieldName);
//                    setMap.put(genSql.getIsPkCol(), column::setIsPk);
//                    setMap.put(genSql.getIsRequiredCol(), column::setIsRequired);
//
//                    //获取最后单元格num，即总单元格数 ***注意：此处从1开始计数***
//                    int maxRol = sheet.getRow(row).getLastCellNum();
////                    System.out.println("--------第" + row + "行的数据如下--------");
//                    for (int rol = 0; rol < maxRol; rol++) {
//                        String data = sheet.getRow(row).getCell(rol).toString();
//                        if (StringUtils.isEmpty(data)) {
//                            continue;
//                        }
//
//                        int finalRol = rol;
//                        if (row == 0) {
//                            indexMap.keySet().stream()
//                                    .filter(k -> StrUtil.containsAnyIgnoreCase(data.trim(), k.trim()))
//                                    .forEach(k -> indexMap.put(k, finalRol));
//                        } else {
//                            String fieldName = indexMap.entrySet().stream().filter(e -> finalRol == e.getValue())
//                                    .map(Map.Entry::getKey).findFirst().orElse("");
//                            if (StringUtils.isNotEmpty(fieldName)) {
//                                setMap.get(fieldName).accept(data);
//                                ;
//                            }
//                        }
//                    }
//                    if (row > 0) {
//                        genTableColumnPOList.add(column);
//                    }
//                }
//
//            }
//
//            for (GenTableColumnPO column : genTableColumnPOList) {
////                column.setFieldName(tableGeneralHandlerMap.get(ColumnConversionStrategy.PIN_YIN)
////                        .toFieldName(column.getColumnComment(), column.getColumnName()));
//                column.setFieldType(tableDialectHandlerMap.get(DbType.MYSQL).toJavaFieldType(column.getColumnType()));
//            }
//            return genTableColumnPOList;
//        } catch (IOException e) {
//            e.printStackTrace();
//        }
//        return new ArrayList<>();
//    }

    @Override
    public IPage<GenTableResp> pageGenTable(GenTablePageReq req) {
        IPage<GenTablePO> page = genTableRepository.pageGenTable(req);
        List<Long> datasourceIds = page.getRecords().stream().map(GenTablePO::getDatasourceId)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(datasourceIds)) {
            return new Page<>();
        }
        Map<Long, String> datasourceMap = datasourceConfigRepository.listByIds(datasourceIds).stream()
                .collect(Collectors.toMap(DatasourceConfigPO::getId, DatasourceConfigPO::getName, (k1, k2) -> k1));
        List<GenTableResp> respList = page.getRecords().stream().map(genTableAssembly::toGenTableRespFrom)
                .peek(x -> x.setDatasourceName(datasourceMap.get(x.getDatasourceId())))
                .collect(Collectors.toList());
        return new Page<GenTableResp>(page.getCurrent(), page.getSize(), page.getTotal()).setRecords(respList);
    }

    @Override
    @Transactional
    public List<ImportTableResp> importDbTable(ImportDbTableReq req) {
        if (CollectionUtils.isEmpty(req.getTableNames())) {
            return new ArrayList<>();
        }
        DatasourceConfigPO datasourceConfigPO = datasourceConfigRepository.getById(req.getDatasourceId());
        Assert.notNull(datasourceConfigPO, "数据源不存在");
        GeneratorConfig generatorConfig = datasourceConfigAssembly.toGeneratorConfigFrom(datasourceConfigPO);
        List<SQLContext> contextList = this.buildSQLContextList(req.getTableNames(), generatorConfig);
        List<ImportTable> importTableList = new ArrayList<>();
        for (SQLContext sqlContext : contextList) {
            ImportTable importTable = new ImportTable();
            GenTablePO.GenTablePOBuilder genTablePOBuilder = GenTablePO.builder();
            TableDefinition tableDefinition = sqlContext.getTableDefinition();
            GenTablePO genTablePO = genTablePOBuilder.tableName(tableDefinition.getTableName())
                    .datasourceId(req.getDatasourceId())
                    .tplCategory(GenType.CRUD.getCode())
                    .tableComment(tableDefinition.getComment()).columns(new ArrayList<>()).build();

            for (ColumnDefinition item : tableDefinition.getColumnDefinitions()) {
                GenTableColumnPO build = GenTableColumnPO.builder()
                        .columnLength(item.getLength()).columnScale(item.getScale())
                        .columnName(item.getColumnName()).columnType(item.getType())
                        .columnComment(item.getComment())
                        .isNullable(item.getIsNullable())
                        .sort(item.getSortNo())
                        .isPk(item.getIsPk())
                        .build();
                importTable.getGenTableColumnList().add(build);
            }
            List<TableIndexDefinition> tableIndexDefinitions = tableDefinition.getTableIndexDefinitions();
            for (TableIndexDefinition item : tableIndexDefinitions) {
                importTable.getGenTableIndexList().add(genTableAssembly.toGenTableIndex(item));
            }
            importTable.setGenTable(genTablePO);
            importTableList.add(importTable);
        }
        return batchSaveGenTable(importTableList);
    }

    /**
     * 返回SQL上下文列表
     *
     * @param tableNames 表名
     * @param generatorConfig 配置
     * @return 返回SQL上下文
     */
    private List<SQLContext> buildSQLContextList(List<String> tableNames, GeneratorConfig generatorConfig) {
        List<SQLContext> contextList = new ArrayList<>();
        SQLService service = SQLServiceFactory.build(generatorConfig);

        TableSelector tableSelector = service.getTableSelector(generatorConfig);
        tableSelector.setSchTableNames(tableNames);

        List<TableDefinition> tableDefinitions = tableSelector.getTableDefinitions();

        for (TableDefinition tableDefinition : tableDefinitions) {
            SQLContext sqlContext = new SQLContext(tableDefinition);
            sqlContext.setDbName(generatorConfig.getDbName());
            contextList.add(sqlContext);
        }
        return contextList;
    }

    @Override
    @Transactional
    public void deleteGenTableByIds(List<Long> tableIds) {
        if (!genTableRepository.removeByIds(tableIds)) {
            throw new BizException("删除表数据失败");
        }
        genTableColumnRepository.removeByTableIds(tableIds);
        genTableIndexRepository.deleteBatchByTableIds(tableIds);
    }

    @Override
    public GenTableResp getGenTableRespByTableId(Long tableId) {
        GenTablePO genTablePO = genTableRepository.getById(tableId);
        Assert.notNull(genTablePO, "表不存在");
        return genTableAssembly.toGenTableRespFrom(genTablePO);
    }

    @Override
    @Transactional
    public void saveOrUpdateGenTableInfo(GenTableInfoSaveReq req) {
        if (Objects.nonNull(req.getGenTable()) && Objects.nonNull(req.getGenTable().getId())) {
            genTableRepository.updateById(req.getGenTable());
        }
        genTableColumnRepository.saveOrUpdateBatch(req.getColumnList());

        List<GenTableIndexReq> indexList = req.getIndexList();
        List<GenTableIndexPO> genTableIndexList = null;
        if (CollectionUtils.isNotEmpty(indexList)) {
            genTableIndexList = indexList.stream().map(genTableAssembly::toGenTableIndex).collect(Collectors.toList());
         }
        genTableIndexRepository.saveOrUpdateBatch(genTableIndexList);
    }

    @Override
    public GenInfoResp getGenInfo(Long tableId) {
        GenTablePO genTablePO = genTableRepository.getById(tableId);
        if (Objects.isNull(genTablePO)) {
            return null;
        }
        DatasourceConfigPO datasourceConfigPO = datasourceConfigRepository.getById(genTablePO.getDatasourceId());
        GenInfoResp genInfoResp = new GenInfoResp();
        genTableAssembly.updateGenInfoResp(datasourceConfigPO, genInfoResp);
        genTableAssembly.updateGenInfoResp(genTablePO, genInfoResp);
        return genInfoResp;
    }
}
